package com.java.houpe.ListBeanUtils;

import com.sun.istack.internal.Nullable;
import lombok.SneakyThrows;
import org.slf4j.Logger;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;

import java.beans.PropertyDescriptor;
import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Supplier;

/**
 * @author Blues
 * <p>
 * demo
 * List<Article> adminEntityList = articleMapper.getAllArticle();
 * List<ArticleVo> articleVoList = ColaBeanUtils.copyListProperties(adminEntityList , ArticleVo::new, (articleEntity, articleVo) -> {
 * // 回调处理
 * });
 */
public class ListBeanUtils extends BeanUtils {

    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target) {
        return copyListProperties(sources, target, null, null);
    }

    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, ListBeanUtilsCallBack<S, T> callBack) {
        return copyListProperties(sources, target, null, callBack);
    }

    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, List<RuleMap> rule) {
        return copyListProperties(sources, target, rule, null);
    }


    /**
     * @author Blues
     * 如：List<AdminEntity> 赋值到 List<AdminVo> ，List<AdminVo>中的 AdminVo 属性都会被赋予到值
     * S: 数据源类 ，T: 目标类::new(eg: AdminVo::new)
     */
    public static <S, T> List<T> copyListProperties(List<S> sources, Supplier<T> target, List<RuleMap> rule, ListBeanUtilsCallBack<S, T> callBack) {
        List<T> list = new ArrayList<>(sources.size());
        for (S source : sources) {
            T t = target.get();
            copyProperties(source, t);
            if (callBack != null) {
                // 回调
                callBack.callBack(source, t);
            }
            list.add(t);
        }
        return list;
    }

    @SneakyThrows
    public static <T, R> List<R> getArrayListKeys(List<T> list, String key, R classType) {
        if (list.get(0) == null) {
            return null;
        }
        Class tClass = list.get(0).getClass();
        Method tm = tClass.getDeclaredMethod("get" + key.substring(0, 1).toUpperCase() + key.substring(1));

        ArrayList<R> result = new ArrayList<>();
        for (T t : list) {
            result.add((R) tm.invoke(t));
        }
        return result;
    }

    @SneakyThrows
    /**
     * 返回key匹配的一个
     */
    public static <S, Z> S findObject(List<S> target, Z sourceId, String targetKey) {
        if (target.get(0) == null) {
            return null;
        }
        Class tClass = target.get(0).getClass();
        Method tm = tClass.getDeclaredMethod("get" + targetKey.substring(0, 1).toUpperCase() + targetKey.substring(1));

        for (S s : target) {
            if (tm.invoke(s).equals(sourceId)) {
                return s;
            }
        }
        return null;
    }

    @SneakyThrows
    /**
     * 返回key匹配的所有
     */
    public static <S, Z> List<S> findObjects(List<S> target, Z sourceId, String targetKey) {
        if (target.get(0) == null) {
            return null;
        }

        Class tClass = target.get(0).getClass();
        Method tm = tClass.getDeclaredMethod("get" + targetKey.substring(0, 1).toUpperCase() + targetKey.substring(1));

        List<S> result = new ArrayList<>();
        for (S s : target) {
            if (tm.invoke(s).equals(sourceId)) {
                result.add(s);
            }
        }
        return result;
    }

    @SneakyThrows
    public static void copyPropertiesWithRelation(Object source, Object target, @Nullable Class<?> editable,
                                                  @Nullable String... ignoreProperties) throws BeansException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Assert.notNull(source, "Source must not be null");
        Assert.notNull(target, "Target must not be null");

        Class<?> actualEditable = target.getClass();
        if (editable != null) {
            if (!editable.isInstance(target)) {
                throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
                        "] not assignable to Editable class [" + editable.getName() + "]");
            }
            actualEditable = editable;
        }

        PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);

        HashMap<String, String> keyAnaMap = new HashMap<>();
        HashMap<String, String> temp = new HashMap<>();
        HashMap<String, SourceServicesObj> serviceAnaMap = new HashMap<>();

        for (Field field : actualEditable.getDeclaredFields()) {
            SourceKey anaKey = field.getAnnotation(SourceKey.class);
            if (anaKey != null) {
                keyAnaMap.put(field.getName(), anaKey.value());
            }

            SourceServices anaKeys = field.getAnnotation(SourceServices.class);
            if (anaKeys != null) {

                serviceAnaMap.put(field.getName(),
                        SourceServicesObj.builder()
                                .method(anaKeys.method())
                                .className(anaKeys.className())
                                .queryField(anaKeys.queryField()).build());
            }
        }
        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);

        //sourcekey对应的属性copy
        for (PropertyDescriptor targetPd : targetPds) {
            Method writeMethod = targetPd.getWriteMethod();
            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {
                //获取注解指定的对应的sourceDto 字段 如果为空，则使用属性名匹配
                PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(),
                        keyAnaMap != null && keyAnaMap.get(targetPd.getName()) != null ? keyAnaMap.get(targetPd.getName()) : targetPd.getName());

                if (null != sourcePd) {
                    Method readMethod = sourcePd.getReadMethod();
                    if (readMethod != null &&
                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
                        try {
                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
                                readMethod.setAccessible(true);
                            }
                            Object value = readMethod.invoke(source);
                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                                writeMethod.setAccessible(true);
                            }
                            writeMethod.invoke(target, value);
                        } catch (Throwable ex) {
                            throw new FatalBeanException(
                                    "Could not copy property '" + targetPd.getName() + "' from source to target", ex);
                        }
                    }

                }
            }
        }

        //SourceServices注解主外键属性copy
        for (PropertyDescriptor targetPd : targetPds) {
            if (serviceAnaMap.containsKey(targetPd.getName())) {
                SourceServicesObj obj = serviceAnaMap.get(targetPd.getName());

                //创建的service
                Class serviceClass = obj.getClassName();
                Method servicesMethod = ReflectUtils.getMethodByName(serviceClass, obj.getMethod());

                //获取 target 查询queryField字段的值
                String queryField = obj.getQueryField();
                Field field = target.getClass().getDeclaredField(queryField);

                //获得target Method
                Method writeMethod = targetPd.getWriteMethod();

                //获得target对象上需要查询的值及类型转换
                Object queryFiledVal = field.get(target);

                Object queryFiledValReslut = null;
                if (ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], servicesMethod.getReturnType())) {

                    //services根据val查询
                    queryFiledValReslut = servicesMethod.invoke((Object) serviceClass.newInstance(), queryFiledVal);

                    //查询结果赋值到target关联的属性
                    if (writeMethod != null && (ignoreList == null || !ignoreList.contains(targetPd.getName()))) {

                        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
                            writeMethod.setAccessible(true);
                        }
                        writeMethod.invoke(target, queryFiledValReslut);
                    }
                }
                else{
                    throw new IllegalArgumentException("外部调用services插入失败："+obj.getClassName()+" 调用的 "+obj.getMethod()+" 方法返回参数不匹配,类型应为："+writeMethod.getParameterTypes()[0]+" 实际传入为："+servicesMethod.getReturnType());
                }
            }
        }
        System.out.println(target);

    }
    /**
     * 使用注解转换
     * @param sources
     * @param target
     * @param <S>
     * @param <T>
     * @return
     */
//    public static <S, T> List<T> copyListPropertiesWithRelation(List<S> sources, Supplier<T> target,@Nullable String... ignoreProperties) {
//
//        Assert.notNull(sources, "sources must not be null");
//        Assert.notNull(target, "target must not be null");
//
//        Class<?> actualEditable = target.getClass();
//
//        PropertyDescriptor[] targetPds = BeanUtils.getPropertyDescriptors(actualEditable);
//        List<String> ignoreList = (ignoreProperties != null ? Arrays.asList(ignoreProperties) : null);
//
//
//        HashMap<String,String> copRelationShip=new HashMap<>();
//
//        for (Field field : target.getClass().getDeclaredFields()) {
//            //获取包含sourekey的注解
//            Relation annotation = field.getAnnotation(Relation.class);
//            if (null!=annotation.sourceKey()){
//
//                copRelationShip.put(field.getName(), annotation.sourceKey());
//            }
//            //获取包含targetkey的注解
//            if (null!=annotation.targetKey()){
//
//                copRelationShip.put(field.getName(), annotation.targetKey());
//            }
//
//
//        }
//
//
//
//
//
//
//        for (PropertyDescriptor targetPd : targetPds) {
//            Method writeMethod = targetPd.getWriteMethod();
//
//            String name = targetPd.getName();
//
//            String sourceName = copRelationShip.get(name);
//            if (sourceName != null) name = sourceName;
//            if (writeMethod != null && (ignoreList == null || !ignoreList.contains(name))) {
////////////////////////////////////
//                PropertyDescriptor sourcePd = BeanUtils.getPropertyDescriptor(source.getClass(), name);
//                if (sourcePd != null) {
//                    Method readMethod = sourcePd.getReadMethod();
//                    if (readMethod != null &&
//                            ClassUtils.isAssignable(writeMethod.getParameterTypes()[0], readMethod.getReturnType())) {
//                        try {
//                            if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
//                                readMethod.setAccessible(true);
//                            }
//                            Object value = readMethod.invoke(source);
//                            if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
//                                writeMethod.setAccessible(true);
//                            }
//                            writeMethod.invoke(target, value);
//                        } catch (Throwable ex) {
//                            throw new FatalBeanException(
//                                    "Could not copy property '" + name + "' from source to target", ex);
//                        }
//                    }
//                }
//    ////////////////////////////////
//
//
//
//
//
//            }
//
//
//
//
//
//
//
//        return copyListProperties(sources, target, null, null);
//
////        try {
////            Class clazz = target.getClass();
////
////            if (clazz.isAnnotationPresent(Relation.class)) {
////
////                Relation info = (Relation) clazz.getAnnotation(Relation.class);
////                if (null!=info.sourceKey()&&null!=info.targetKey()){
////
//////                    return copyListProperties(sources, target, null, null);
////                }
////                else {
////                    throw new Exception("");
////                }
////
////            }
////        } catch (Exception e) {
////            e.printStackTrace();
////        }
//
//
//
//    }

    /**
     * 利用注解补充完整
     *
     * @param t
     * @param <T>
     * @return
     */
    public static <T> T fill(T t) {
        return null;
    }

    /**
     * 补充指定的注解
     *
     * @param t
     * @param strings
     * @param <T>
     * @return
     */
    public static <T> T fill(T t, String... strings) {
        return null;
    }
}

